/* Simple HTTP Server Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <sys/param.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "nvs.h"
#include "nvs_flash.h"

#include "cJSON.h"

#include <esp_http_server.h>

#include "app_mqtt.h"
#include "app_main.h"
#include "storage.h"


extern const uint8_t index_html_start[] asm("_binary_index_html_start");
extern const uint8_t index_html_end[]   asm("_binary_index_html_end");

extern const uint8_t mqtt_setting_html_start[] asm("_binary_mqttSetting_html_start");
extern const uint8_t mqtt_setting_html_end[]   asm("_binary_mqttSetting_html_end");

extern const uint8_t user_setting_html_start[] asm("_binary_userSetting_html_start");
extern const uint8_t user_setting_html_end[]   asm("_binary_userSetting_html_end");

extern const uint8_t device_info_html_start[] asm("_binary_deviceInfo_html_start");
extern const uint8_t device_info_html_end[]   asm("_binary_deviceInfo_html_end");


extern const uint8_t loginerror_html_start[] asm("_binary_loginerror_html_start");
extern const uint8_t loginerror_html_end[]   asm("_binary_loginerror_html_end");

extern const uint8_t mqttParamSaveFail_html_start[] asm("_binary_mqttParamSaveFail_html_start");
extern const uint8_t mqttParamSaveFail_html_end[]   asm("_binary_mqttParamSaveFail_html_end");

extern const uint8_t userPasswordSaveFail_html_start[] asm("_binary_userPasswordSaveFail_html_start");
extern const uint8_t userPasswordSaveFail_html_end[]	asm("_binary_userPasswordSaveFail_html_end");


#define DEFAULT_LOGIN_NAME				"admin"
#define DEFAULT_LOGIN_PWD				"12345678"

static const char *TAG="APP_http_server";

/* An HTTP GET handler */
esp_err_t hello_get_handler(httpd_req_t *req)
{
    char*  buf;
    size_t buf_len;

    /* Get header value string length and allocate memory for length + 1,
     * extra byte for null termination */
    buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        /* Copy null terminated value string into buffer */
        if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
            ESP_LOGI(TAG, "Found header => Host: %s", buf);
        }
        free(buf);
    }

    buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-2") + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if (httpd_req_get_hdr_value_str(req, "Test-Header-2", buf, buf_len) == ESP_OK) {
            ESP_LOGI(TAG, "Found header => Test-Header-2: %s", buf);
        }
        free(buf);
    }

    buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-1") + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if (httpd_req_get_hdr_value_str(req, "Test-Header-1", buf, buf_len) == ESP_OK) {
            ESP_LOGI(TAG, "Found header => Test-Header-1: %s", buf);
        }
        free(buf);
    }

    /* Read URL query string length and allocate memory for length + 1,
     * extra byte for null termination */
    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            ESP_LOGI(TAG, "Found URL query => %s", buf);
            char param[32];
            /* Get value of expected key from query string */
            if (httpd_query_key_value(buf, "query1", param, sizeof(param)) == ESP_OK) {
                ESP_LOGI(TAG, "Found URL query parameter => query1=%s", param);
            }
            if (httpd_query_key_value(buf, "query3", param, sizeof(param)) == ESP_OK) {
                ESP_LOGI(TAG, "Found URL query parameter => query3=%s", param);
            }
            if (httpd_query_key_value(buf, "query2", param, sizeof(param)) == ESP_OK) {
                ESP_LOGI(TAG, "Found URL query parameter => query2=%s", param);
            }
        }
        free(buf);
    }

    /* Set some custom headers */
    httpd_resp_set_hdr(req, "Custom-Header-1", "Custom-Value-1");
    httpd_resp_set_hdr(req, "Custom-Header-2", "Custom-Value-2");

    /* Send response with custom headers and body set as the
     * string passed in user context*/
    const char* resp_str = (const char*) req->user_ctx;
    httpd_resp_send(req, resp_str, strlen(resp_str));

    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
        ESP_LOGI(TAG, "Request headers lost");
    }
    return ESP_OK;
}

esp_err_t index_get_handler(httpd_req_t *req)
{
	httpd_resp_send(req, (char *)&index_html_start, strlen((char *)&index_html_start));    
	return ESP_OK;
}

esp_err_t mqttSetting_get_handler(httpd_req_t *req)
{
	httpd_resp_send(req, (char *)&mqtt_setting_html_start, strlen((char *)&mqtt_setting_html_start));    
	return ESP_OK;
}

esp_err_t userSetting_get_handler(httpd_req_t *req)
{
	httpd_resp_send(req, (char *)&user_setting_html_start, strlen((char *)&user_setting_html_start));    
	return ESP_OK;
}

esp_err_t deviceInfo_get_handler(httpd_req_t *req)
{
	httpd_resp_send(req, (char *)&device_info_html_start, strlen((char *)&device_info_html_start));    
	return ESP_OK;
}

esp_err_t mqttValue_get_handler(httpd_req_t *req)
{
	cJSON* cJson_post = cJSON_CreateObject();
	cJSON_AddStringToObject( cJson_post, "host", DeviceConfig.mqttDomain );

	char portStr[6];
	sprintf( portStr, "%d", DeviceConfig.mqttPort );
	cJSON_AddStringToObject( cJson_post, "port", portStr );

	cJSON_AddStringToObject( cJson_post, "username", DeviceConfig.mqttUsername );
	cJSON_AddStringToObject( cJson_post, "password", DeviceConfig.mqttPassword );
	char *json_data = cJSON_Print( cJson_post );
	cJSON_Delete(cJson_post);
	printf("%s\n",json_data);

    /* Send response with custom headers and body set as the
     * string passed in user context*/
    const char* resp_str = json_data;
    httpd_resp_send(req, resp_str, strlen(resp_str));

    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
        ESP_LOGI(TAG, "Request headers lost");
    }
	
	cJSON_free( json_data ); 
	
    return ESP_OK;

}

esp_err_t deviceInfoValue_get_handler(httpd_req_t *req)
{
	char strState[10];
	uint8_t state;
	
	RelayStateCheck( &state );

	if( state == 0 )
	{
		strcpy(strState, "off");
	}
	else if( state == 1 )
	{
		strcpy(strState, "on");
	}
	else
	{
		strcpy(strState, "unkow");
	}
	
	uint8_t MAC[6];
	char strMac[13];
	if( esp_wifi_get_mac(ESP_IF_WIFI_STA, MAC ) == ESP_OK )
	{
		sprintf( strMac, "%02X%02X%02X%02X%02X%02X", MAC[0], MAC[1], MAC[2], MAC[3], MAC[4], MAC[5] );
	}
	
	cJSON* cJson_post = cJSON_CreateObject();

	cJSON_AddStringToObject( cJson_post, "State", strState );

	cJSON_AddStringToObject( cJson_post, "Mac", strMac );

	cJSON_AddStringToObject( cJson_post, "softVer", AppVer );
	char *json_data = cJSON_Print( cJson_post );
	cJSON_Delete(cJson_post);
	printf("%s\n",json_data);

    /* Send response with custom headers and body set as the
     * string passed in user context*/
    const char* resp_str = json_data;
    httpd_resp_send(req, resp_str, strlen(resp_str));

    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
        ESP_LOGI(TAG, "Request headers lost");
    }
	
	cJSON_free( json_data ); 
	
    return ESP_OK;

}


httpd_uri_t login = {
	.uri	   = "/",
    .method    = HTTP_GET,
    .handler   = index_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL
};


httpd_uri_t mqttSetting = {
	.uri	   = "/mqttSetting",
    .method    = HTTP_GET,
    .handler   = mqttSetting_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL
};

httpd_uri_t userSetting = {
	.uri	   = "/userSetting",
    .method    = HTTP_GET,
    .handler   = userSetting_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL
};

httpd_uri_t deviceInfo = {
	.uri	   = "/deviceInfo",
    .method    = HTTP_GET,
    .handler   = deviceInfo_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL
};

httpd_uri_t mqttValue = {
	.uri	   = "/mqttValue",
    .method    = HTTP_GET,
    .handler   = mqttValue_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL
};

httpd_uri_t deviceInfoValue = {
	.uri	   = "/deviceInfoValue",
    .method    = HTTP_GET,
    .handler   = deviceInfoValue_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = NULL
};

/* An HTTP POST handler */
esp_err_t verify_post_handler(httpd_req_t *req)
{
    char buf[100];
    int ret, remaining = req->content_len;

    while (remaining > 0) {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf,
                        MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        /* Send back the same data */
//        httpd_resp_send_chunk(req, buf, ret);
        remaining -= ret;

        /* Log data received */
        ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
        ESP_LOGI(TAG, "ret;%d,%.*s", ret, ret, buf);
        ESP_LOGI(TAG, "====================================");

		buf[ret] = '\0';
		char loginNameValue[21];
		char loginPwdValue[21];

		memset( loginNameValue, 0x00, 21 );
		memset( loginPwdValue, 0x00, 21 );
		if( (httpd_query_key_value( (char *)buf, "loginName",loginNameValue ,sizeof(loginNameValue)) == ESP_OK) &&
			(httpd_query_key_value( (char *)buf, "loginPwd",loginPwdValue ,sizeof(loginPwdValue)) == ESP_OK) )
		{
			ESP_LOGI(TAG, "loginName:%s", loginNameValue );
			ESP_LOGI(TAG, "loginPwd:%s", loginPwdValue );

			if( (strcmp( loginNameValue, DEFAULT_LOGIN_NAME ) == 0)&&(strcmp( loginPwdValue, DeviceConfig.loginPassword ) == 0) )
			{
				httpd_resp_send(req, (char *)&mqtt_setting_html_start, strlen((char *)&mqtt_setting_html_start));
			}
			else
			{
				httpd_resp_send(req, (char *)&loginerror_html_start, strlen((char *)&loginerror_html_start));
				ESP_LOGI(TAG, "loginName or loginPwd error");;
			}
		}
		else
		{
			ESP_LOGI(TAG, "query key fail!");
		}
    }
	
    return ESP_OK;
}

httpd_uri_t verify = {
    .uri       = "/verify",
    .method    = HTTP_POST,
    .handler   = verify_post_handler,
    .user_ctx  = NULL
};

/* An HTTP POST handler */
esp_err_t mqttParamSave_post_handler(httpd_req_t *req)
{
    char buf[100];
    int ret, remaining = req->content_len;

    while (remaining > 0) {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf,
                        MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        /* Send back the same data */
//        httpd_resp_send_chunk(req, buf, ret);
        remaining -= ret;

        /* Log data received */
        ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
        ESP_LOGI(TAG, "ret;%d,%.*s", ret, ret, buf);
        ESP_LOGI(TAG, "====================================");

		buf[ret] = '\0';

		char mqttDomain[64];
		char mqttPort[6];
		char mqttUsername[65];
		char mqttPassword[65];

		if( (httpd_query_key_value( (char *)buf, "mqttHost",mqttDomain ,sizeof(mqttDomain)) == ESP_OK) &&
			(httpd_query_key_value( (char *)buf, "mqttPort",mqttPort ,sizeof(mqttPort)) == ESP_OK) &&
			(httpd_query_key_value( (char *)buf, "mqttUsername",mqttUsername ,sizeof(mqttUsername)) == ESP_OK) &&
			(httpd_query_key_value( (char *)buf, "mqttPassword",mqttPassword ,sizeof(mqttPassword)) == ESP_OK) )
		{
			ESP_LOGI(TAG, "mqttHost:%s", mqttDomain );
			ESP_LOGI(TAG, "mqttPort:%s", mqttPort );
			ESP_LOGI(TAG, "mqttUsername:%s", mqttUsername );
			ESP_LOGI(TAG, "mqttPassword:%s", mqttPassword );
			
			strcpy( DeviceConfig.mqttDomain, mqttDomain );
			DeviceConfig.mqttPort = atoi(mqttPort);
			strcpy( DeviceConfig.mqttUsername, mqttUsername );
			strcpy( DeviceConfig.mqttPassword, mqttPassword );

			if( SPIFFS_InitAndmounted() == true )
			{
				if( SetDeviceConfig( &DeviceConfig ) == true )
				{
					ESP_LOGI(TAG, "set device config ok!");
					httpd_resp_send(req, (char *)&mqtt_setting_html_start, strlen((char *)&mqtt_setting_html_start));

					mqtt_app_Restart();
				}
				else
				{
					ESP_LOGI(TAG, "set device config fail!");
					httpd_resp_send(req, (char *)&mqttParamSaveFail_html_start, strlen((char *)&mqttParamSaveFail_html_start));
				}
				SPIFFS_Unmounted();
			}
		}
		else
		{
			ESP_LOGI(TAG, "query key fail!");
			httpd_resp_send(req, (char *)&mqttParamSaveFail_html_start, strlen((char *)&mqttParamSaveFail_html_start));
		}
    }
	
    return ESP_OK;
}

httpd_uri_t mqttParamSave = {
    .uri       = "/mqttParamSave",
    .method    = HTTP_POST,
    .handler   = mqttParamSave_post_handler,
    .user_ctx  = NULL
};

/* An HTTP POST handler */
esp_err_t userPasswordSave_post_handler(httpd_req_t *req)
{
    char buf[100];
    int ret, remaining = req->content_len;

    while (remaining > 0) {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf,
                        MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        /* Send back the same data */
//        httpd_resp_send_chunk(req, buf, ret);
        remaining -= ret;

        /* Log data received */
        ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
        ESP_LOGI(TAG, "ret;%d,%.*s", ret, ret, buf);
        ESP_LOGI(TAG, "====================================");

		buf[ret] = '\0';

		char userPassword[21];

		if( (httpd_query_key_value( (char *)buf, "newPassword",userPassword ,sizeof(userPassword)) == ESP_OK) )
		{
			ESP_LOGI(TAG, "userPassword:%s", userPassword );
			
			strcpy( DeviceConfig.loginPassword, userPassword );

			if( SPIFFS_InitAndmounted() == true )
			{
				if( SetDeviceConfig( &DeviceConfig ) == true )
				{
					ESP_LOGI(TAG, "set device config ok!");
					httpd_resp_send(req, (char *)&user_setting_html_start, strlen((char *)&user_setting_html_start));
				}
				else
				{
					ESP_LOGI(TAG, "set device config fail!");
					httpd_resp_send(req, (char *)&userPasswordSaveFail_html_start, strlen((char *)&userPasswordSaveFail_html_start));
				}
				SPIFFS_Unmounted();
			}
		}
		else
		{
			ESP_LOGI(TAG, "query key fail!");
			httpd_resp_send(req, (char *)&userPasswordSaveFail_html_start, strlen((char *)&userPasswordSaveFail_html_start));
		}
    }
	
    return ESP_OK;
}

httpd_uri_t userPasswordSave = {
    .uri       = "/userPasswordSave",
    .method    = HTTP_POST,
    .handler   = userPasswordSave_post_handler,
    .user_ctx  = NULL
};


/* An HTTP PUT handler. This demonstrates realtime
 * registration and deregistration of URI handlers
 */
esp_err_t ctrl_put_handler(httpd_req_t *req)
{
    char buf;
    int ret;

    if ((ret = httpd_req_recv(req, &buf, 1)) <= 0) {
        if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
            httpd_resp_send_408(req);
        }
        return ESP_FAIL;
    }

    if (buf == '0') {
        /* Handler can be unregistered using the uri string */
        ESP_LOGI(TAG, "Unregistering /hello and /echo URIs");
        httpd_unregister_uri(req->handle, "/hello");
        httpd_unregister_uri(req->handle, "/echo");
    }
    else {
        ESP_LOGI(TAG, "Registering /hello and /echo URIs");
        httpd_register_uri_handler(req->handle, &login);
        httpd_register_uri_handler(req->handle, &verify);
    }

    /* Respond with empty body */
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

httpd_uri_t ctrl = {
    .uri       = "/ctrl",
    .method    = HTTP_PUT,
    .handler   = ctrl_put_handler,
    .user_ctx  = NULL
};

httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
	config.max_uri_handlers = 9;
	config.max_resp_headers = 9;
    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &login);
		httpd_register_uri_handler(server, &mqttSetting);
		httpd_register_uri_handler(server, &userSetting);
		httpd_register_uri_handler(server, &deviceInfo);
		httpd_register_uri_handler(server, &deviceInfoValue);
		httpd_register_uri_handler(server, &mqttValue);
        httpd_register_uri_handler(server, &verify);
		httpd_register_uri_handler(server, &mqttParamSave);
		httpd_register_uri_handler(server, &userPasswordSave);
//        httpd_register_uri_handler(server, &ctrl);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

void stop_webserver(httpd_handle_t server)
{
    // Stop the httpd server
    httpd_stop(server);
}

httpd_handle_t httpServer = NULL;

void disconnect_http_server_handler(void* arg, void* event_data)
{
//    httpd_handle_t* server = (httpd_handle_t*) arg;
    if ( httpServer != NULL ) 
	{
        ESP_LOGI(TAG, "Stopping webserver");
        stop_webserver( httpServer );
        httpServer = NULL;
    }
}

void connect_http_server_handler(void* arg, void* event_data)
{
//	httpd_handle_t* server = (httpd_handle_t*) arg;
    if ( httpServer == NULL) 
	{
        ESP_LOGI(TAG, "Starting webserver");
        httpServer = start_webserver();
    }
}

void http_server_start()
{
//    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
//    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));

//    server = start_webserver();
}
